# -*- coding: utf-8 -*-
"""
******* 文档说明 ******

# 当前项目: recommenders-master
# 创建时间: 2020-09-06  15:18
# 开发作者: Vincent
# 版    本: V1.0
"""
import os
import sys
import logging
import numpy as np
import pandas as pd
from common.timer import Timer
from common.python_splitters import python_stratified_split
from common.python_evaluation import map_at_k, ndcg_at_k, precision_at_k, recall_at_k
from sar_model.sar_singlenode import SARSingleNode as SAR

print("System version: {}".format(sys.version))
print("Pandas version: {}".format(pd.__version__))

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)-8s %(message)s')

# top k items to recommend
TOP_K = 10


# 读取数据
def get_data(data_path):
    ratings_title = ['userID','itemID', 'rating', 'timestamp']
    data = pd.read_table(os.path.join(data_path, 'ratings.dat'), sep='::',
                            header=None, names=ratings_title, engine = 'python')
    data['rating'] = data['rating'].astype(np.float32)
    # print(data.head())

    movies_title = ['MovieID', 'Title', 'Genres']
    movies = pd.read_table(os.path.join(data_path, 'movies.dat'),
                           sep='::', header=None, names=movies_title, engine='python')

    return data, movies


# 打印测试结果
def print_test(y_true, y_pre, item_map, top_k):
    """
    :param y_true:
    :param y_pre:
    :param item_map:
    :param top_k:
    :return:
    """
    y_true = y_true.sort_values(by=['rating'], ascending=False).copy()

    eval_map = map_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)
    eval_ndcg = ndcg_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)
    eval_precision = precision_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)
    eval_recall = recall_at_k(y_true, y_pre, col_user='userID', col_item='itemID', col_rating='rating', k=top_k)

    print('map_at_k:        {:.3f}'.format(eval_map))
    print('ndcg_at_k:       {:.3f}'.format(eval_ndcg))
    print('precision_at_k:  {:.3f}'.format(eval_precision))
    print('recall_at_k:     {:.3f}\n'.format(eval_recall))

    # 真实值 Item 字典
    y_true_item = {v: i for i, v in enumerate(y_true['itemID'])}

    for i in range(len(y_true)):
        item_i = y_true['itemID'].iloc[i]
        rating_i = y_true['rating'].iloc[i]

        print('{:3s} {:5d}  {:.3f}  {}      {}'.format('', item_i, rating_i,
                                                       item_map.loc[[item_i], ['Title']].values[0][0],
                                                       item_map.loc[[item_i], ['Genres']].values[0][0]))

    print('- '*20, 'Predict', ' -'*20)
    for i in range(len(y_pre)):
        item_i = y_pre['itemID'].iloc[i]
        rating_i = y_pre['prediction'].iloc[i]

        top_i = str(y_true_item[item_i]) if item_i in y_true_item else ''

        print('{:3s} {:5d}  {:.3f}  {}      {}'.format(top_i, item_i, rating_i,
                                                       item_map.loc[[item_i], ['Title']].values[0][0],
                                                       item_map.loc[[item_i], ['Genres']].values[0][0]))


def main():
    # 读取数据
    data, movies_map = get_data(data_path = r'D:\Data\Recommend\ml-1m')
    data = data[:1000]

    # 拆分训练集、测试集 分组拆开
    train, test = python_stratified_split(data, ratio=0.75, col_user='userID', col_item='itemID', seed=42)
    print('TrainData: {}\n{}'.format(train.shape, train.head()))
    print('TestData : {}\n{}'.format(test.shape, test.head()))

    model = SAR(
        col_user="userID",
        col_item="itemID",
        col_rating="rating",
        col_timestamp="timestamp",
        similarity_type="jaccard",
        time_decay_coefficient=30,
        timedecay_formula=True,
        normalize=True
    )

    # # # ########### 训练
    with Timer() as train_time:
        model.fit(train)

    print("Took {} seconds for training.".format(train_time.interval))


    # ########### 预测
    with Timer() as test_time:
        y_predict = model.recommend_k_items(test, remove_seen=True)

    print("Took {} seconds for prediction.".format(test_time.interval))

    eval_map = map_at_k(test, y_predict, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)
    print(eval_map)

    # 打印测试结果
    print_test(test[test['userID'] == 7], y_predict[y_predict['userID'] == 7], movies_map, top_k=TOP_K)

    # print(' -------------------------------------- ')
    # temp_df = test[(test['userID'] == 1) | (test['userID'] == 2) | (test['userID'] == 3)].copy()
    # temp_df = temp_df[temp_df['itemID'] > 2000].copy()
    #
    # y_predict = model.recommend_k_items(test, remove_seen=True)
    # # model.fit(temp_df)
    #
    # model.set_index(temp_df)
    #
    # # add mapping of user and item ids to indices   添加用户和项id到索引的映射
    # temp_df.loc[:, model.col_item_id] = temp_df['itemID'].apply(lambda item: model.item2index.get(item, np.NaN))
    # temp_df.loc[:, model.col_user_id] = temp_df['userID'].apply(lambda user: model.user2index.get(user, np.NaN))
    #
    # user_affinity = model.compute_affinity_matrix(df=temp_df, rating_col='rating')
    #
    # print(user_affinity)
    # print(user_affinity.toarray())
    #
    # # 共现矩阵
    # item_cooccurrence = model.compute_coocurrence_matrix(df=temp_df)
    #
    # from common.python_utils import jaccard
    # jaccard(item_cooccurrence)

if __name__ == '__main__':
    main()
    print(os.path.abspath(os.path.dirname(__file__)))
